﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Rvz.Game.Main.Common;
using Rvz.Game.Main.Entities.Zombies;
using Rvz.Game.Main.Entities.Zombies.States;
using Rvz.Game.Main.State;

namespace Rvz.Game.Main.Rules
{
    public static class ZombieManager
    {


        public static Zombie SpawnZombie(GameState gameState)
        {
            var zombie = new Zombie(gameState.ContentManagerName);
            var randomSpawnPoint = GetRandomMapEdge();
            zombie.X = randomSpawnPoint.X;
            zombie.Y = randomSpawnPoint.Y;
            return zombie;
        }

        public static void ActivateZombies(GameState gameState, IEnumerable<Zombie> zombies)
        {
            var activeZombies = zombies.ToArray();
            activeZombies.ForEach(z => PathZombieToPlayer(gameState, z));
            activeZombies.ForEach(z => BuildStates(z, activeZombies, gameState));
            activeZombies.ForEach(z => z.ProcessStates(gameState));
        }

        private static void BuildStates(Zombie zombie, IEnumerable<Zombie> activeZombies, GameState gameState)
        {
            var robot = gameState.PlayerState.Robot;

            if (zombie.CollisionShape.CollideAgainst(robot.CollisionShape))
            { zombie.AddState(new RobotCollisionState(robot)); }

            activeZombies.Where(x => x != zombie).ForEach(blockingZ =>
            {
                if (zombie.CollisionShape.CollideAgainst(blockingZ.CollisionShape))
                { zombie.AddState(new ZombieCollisionState(blockingZ)); }
            });
        }

        private static void PathZombieToPlayer(GameState gameState, Zombie zombie)
        {
            //NOTE: Naive implementation. Needs more sophistication.
            var playerPosition = gameState.PlayerState.Robot.Position;
            var maxZombieVelocity = zombie.MovementSpeed;
            var diffX = playerPosition.X - zombie.X;
            var diffY = playerPosition.Y - zombie.Y;
            var ratioXtoY = Math.Abs(diffX) / Math.Abs(diffY);
            var ratioYtoX = Math.Abs(diffY) / Math.Abs(diffX);
            var maxXVelocity = ratioXtoY > maxZombieVelocity ? maxZombieVelocity : ratioXtoY;
            var maxYVelocity = ratioYtoX > maxZombieVelocity ? maxZombieVelocity : ratioYtoX;

            var idealSpeed = maxXVelocity + maxYVelocity;
            var smoothRatio = maxZombieVelocity / idealSpeed;
            maxXVelocity = maxXVelocity * smoothRatio;
            maxYVelocity = maxYVelocity * smoothRatio;

            zombie.XVelocity = diffX < 0 ? -maxXVelocity : maxXVelocity;
            zombie.YVelocity = diffY < 0 ? -maxYVelocity : maxYVelocity;

            FacePlayer(gameState, zombie);
        }

        private static Vector2 GetRandomMapEdge()
        {
            var randomYCoordinate = Helper.Random.Next(-Helper.TopYCoordinate, Helper.TopYCoordinate + 1);
            var randomXCoordinate = Helper.Random.Next(-Helper.RightXCoordinate, Helper.RightXCoordinate + 1);

            var side = (ScreenSide)Helper.Random.Next(1, 5);
            switch (side)
            {
                case ScreenSide.Left:
                    var leftXCoordinate = -Helper.RightXCoordinate;
                    return new Vector2(leftXCoordinate, randomYCoordinate);
                case ScreenSide.Top:
                    var topYCoordinate = Helper.TopYCoordinate;
                    return new Vector2(randomXCoordinate, topYCoordinate);
                case ScreenSide.Right:
                    var rightXCoordinate = Helper.RightXCoordinate;
                    return new Vector2(rightXCoordinate, randomYCoordinate);
                case ScreenSide.Bottom:
                    var bottomYCoordinate = -Helper.TopYCoordinate;
                    return new Vector2(randomXCoordinate, bottomYCoordinate);
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private static void FacePlayer(GameState gameState, Zombie zombie)
        {
            var robot = gameState.PlayerState.Robot;
            var angleFromRobot = Math.Atan2(robot.Y - zombie.Y, robot.X - zombie.X);
            const float startingZAdjustment = (float)(Math.PI / 2);
            zombie.RotationZ = (float)angleFromRobot - startingZAdjustment;
        }

        private enum ScreenSide
        {
            Left = 1,
            Top = 2,
            Right = 3,
            Bottom = 4,
        }
    }
}
